/*
	主体结构

	Get方法运行流程：

		                            是
	接收 key --> 检查是否被缓存 -----> 返回缓存值 ⑴
	                |  否                         是
	                |-----> 是否应当从远程节点获取 -----> 与远程节点交互 --> 返回缓存值 ⑵
	                            |  否
	                            |-----> 调用`回调函数`，获取值并添加到缓存 --> 返回缓存值 ⑶
*/

package geecache

import (
	"fmt"
	"log"
	"sync"

	"geecache/geecache/singleflight"
)

// 定义回调函数Getter
type Getter interface {
	Get(key string) ([]byte, error)
}

type GetterFunc func(key string) ([]byte, error)

func (f GetterFunc) Get(key string) ([]byte, error) {
	return f(key)
}

// group 定义
type Group struct {
	name      string
	mainCache cache
	getter    Getter
	peers     PeerPicker

	// 使用singleflight来保证
	// 当key不在缓存中时
	// load key的方法同时只被调用一次
	// 防止缓存击穿
	loader *singleflight.Group
}

// groups 定义
// 用来存储当前所有的group
var (
	mu     sync.RWMutex
	groups = make(map[string]*Group)
)

// 创建Group
// 并加入Groups集合
func NewGroup(name string, cacheSize int64, getter Getter) *Group {
	if getter == nil {
		panic("nil getter")
	}

	// 加锁
	mu.Lock()
	defer mu.Unlock()

	// 创建Group
	g := &Group{
		name:      name,
		mainCache: cache{cacheSize: cacheSize},
		getter:    getter,
		loader:    &singleflight.Group{},
	}

	// 加入Groups
	groups[name] = g

	return g
}

// 查询Group方法
func GetGroup(name string) *Group {
	// 加锁
	mu.RLock()
	defer mu.RUnlock()

	g := groups[name]

	return g
}

// Get方法
func (g *Group) Get(key string) (ByteView, error) {
	// 判断name是否为空
	if key == "" {
		return ByteView{}, fmt.Errorf("key should not be nil")
	}

	// 调用mainCache中的get方法，有则返回
	if value, ok := g.mainCache.get(key); ok {
		log.Println("[GeeCache] hit")
		return value, nil
	}

	// 无则调用load方法，从不同数据源加载
	return g.load(key)
}

// load方法
// 先从本地load,再从远程load
func (g *Group) load(key string) (value ByteView, err error) {
	// 使用singleflight防止load时缓存击穿
	viewi, err := g.loader.Do(key, func() (interface{}, error) {
		// 从远程load
		if g.peers != nil {
			if peer, ok := g.peers.PickPeer(key); ok {
				if value, err := g.getFromPeer(peer, key); err == nil {
					return value, nil
				}
				log.Println("[GeeCache] Failed to get from peer", err)
			}
		}

		// 从本地load
		return g.loadLocal(key)
	})

	if err == nil {
		return viewi.(ByteView), nil
	}

	return
}

// 从本地加载key
func (g *Group) loadLocal(key string) (ByteView, error) {
	// 调用回调函数
	b, err := g.getter.Get(key)
	if err != nil {
		return ByteView{}, err
	}

	bv := ByteView{b: cloneByte(b)}
	// 将缓存存入cache中
	g.populateCache(key, bv)

	return bv, nil
}

// 从远程节点加载
func (g *Group) getFromPeer(peer PeerGetter, key string) (ByteView, error) {
	bytes, err := peer.Get(g.name, key)
	if err != nil {
		return ByteView{}, err
	}

	return ByteView{b: bytes}, nil

}

func (g *Group) populateCache(key string, value ByteView) {
	g.mainCache.add(key, value)
}

// 注册节点
func (g *Group) RegisterPeers(peers PeerPicker) {
	if g.peers != nil {
		panic("Regiester peers called once more")
	}

	g.peers = peers
}
